// 命令模式是最简单和优雅的模式之一， 
// 命令模式中的命令(command) 指的是一个执行某些特定事情的指令。

// 有时候需要向某些对象发送请求，
// 但是并不知道请求的接收者是谁，
// 也不知道被请求的操作是什么。
// 此时希望用一种松耦合的方式来设计程序，
// 使得请求发送者和请求接收者能够消除彼此之间的耦合关系。

// 菜单案例

// 假设我们正在编写一个用户界面程序，
// 该用户界面上至少有数十个 Button 按钮。
// 因为项目比较复杂，所以我们决定让某个程序员负责绘制这些按钮，
// 而另外一些程序员则负责编写点击按钮后的具体行为，这些行为都将被封装在对象里。

// 在大型项目开发中，这是很正常的分工。
// 对于绘制按钮的程序员来说，
// 他完全不知道某个按钮未来将用来做什么，
// 可能用来刷新菜单界面，也可能用来增加一些子菜单，
// 他只知道点击这个 按钮会发生某些事情。
// 那么当完成这个按钮的绘制之后，应该如何给它绑定onclick 事件呢?

// 我们很快可以找到在这里运用命令模式的理由:
// 点击了按钮之后，必须向某些负责具体行为的对象发送请求，
// 这些对象就是请求的接收者。
// 但是目前并不知道接收者是什么对象，也不知道接收者究竟会做什么。
// 此时我们需要借助命令对象的帮助，以便解开按钮和负责具体行为对象之间的耦合。

// 代码实现

var RefreshMenuBarCommand = function(receiver) {
    return {
        execute: function() {
            receiver.refresh();
        }
    }
};

var setCommand = function(button, command) {
    button.onclick = function() {
        command.execute();
    }
};

var refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar);
setCommand(button1, refreshMenuBarCommand);

// 撤销命令
// 命令模式的作用不仅是封装运算块，而且可以很方便地给命令对象增加撤销操作。
// 现在页面上有个小球，点击移动按钮和撤销按钮可以实现移动和撤销的操作。

var MoveCommand = function(receiver, pos) {
    this.receiver = receiver;
    this.pos = pos;
    this.oldPos = null;
};
MoveCommand.prototype.execute = function() {
    this.receiver.start('left', this.pos, 1000, 'strongEaseOut');
    this.oldPos = this.receiver.dom.getBoundingClientRect([this.receiver.propertyName]); // 记录小球开始移动前的位置
};

MoveCommand.prototype.undo = function() {
    this.receiver.start('left', this.oldPos, 1000, 'strongEaseOut'); // 回到小球移动前记录的位置
};

var moveCommand;
moveBtn.onclick = function() {
    var animate = new Animate(ball);
    moveCommand = new MoveCommand(animate, pos.value);
    moveCommand.execute();
};

cancelBtn.onclick = function() {
    moveCommand.undo();
};

// 宏命令
// 宏命令是一组命令的集合，通过执行宏命令的方式，
// 可以一次执行一批命令。想象一下，家里有一个万能遥控器，
// 每天回家的时候，只要按一个特别的按钮，它就会帮我们关上房间门，顺便打开电脑并登录 QQ。

// 代码实现

var closeDoorCommand = {
    execute: function() {
        console.log('关门');
    }
};
var openPcCommand = {
    execute: function() {
        console.log('开电脑');
    }
};
var openQQCommand = {
    execute: function() {
        console.log('登录 QQ');
    }
};
var MacroCommand = function() {
    return {
        commandsList: [],
        add: function(command) {
            this.commandsList.push(command);
        },
        execute: function() {
            for (var i = 0, command; command = this.commandsList[i++];) {
                command.execute();
            }
        }
    }
};
var macroCommand = MacroCommand();

macroCommand.add(closeDoorCommand);
macroCommand.add(openPcCommand);
macroCommand.add(openQQCommand);

macroCommand.execute();